`include "common.svh"
`define REGFILE_DIFFTEST

module regfile_wrapper #(
    parameter READ_PORTS  = 4,
    parameter WRITE_PORTS = 2
) (

    input clk,

    input  PRF_IDX raddr[READ_PORTS-1:0],
    output word_t  rdata[READ_PORTS-1:0],

    input wvalid[WRITE_PORTS-1:0],
    input PRF_IDX waddr[WRITE_PORTS-1:0],
    input word_t wdata[WRITE_PORTS-1:0]
);
  word_t rf_rdata[READ_PORTS-1:0];
  regfile #(READ_PORTS, WRITE_PORTS) inst_regfile (
      .rdata(rf_rdata),
      .*
  );

  genvar gi;
  generate
    word_t bypass_data [READ_PORTS-1:0];
    logic  bypass_valid[READ_PORTS-1:0];
    for (gi = 0; gi < READ_PORTS; gi = gi + 1) begin : BypassMUX
      logic [WRITE_PORTS-1:0] bypass;
      always_comb begin
        integer i;
        for (i = 0; i < WRITE_PORTS; i = i + 1) begin
          bypass[i] = wvalid[i] & (waddr[i] == raddr[gi]);
        end
      end
      assign bypass_valid[gi] = |bypass;
      MUX_OH #(WRITE_PORTS, `WORD_BITS) mux_bypass_data (
          .sel (bypass),
          .din (wdata),
          .dout(bypass_data[gi])
      );
      assign rdata[gi] = bypass_valid[gi] ? bypass_data[gi] : rf_rdata[gi];
    end
  endgenerate

endmodule

module regfile #(
    parameter READ_PORTS  = 4,
    parameter WRITE_PORTS = 2

) (
    input clk,

    input  PRF_IDX raddr[READ_PORTS-1:0],
    output word_t  rdata[READ_PORTS-1:0],

    input wvalid[WRITE_PORTS-1:0],
    input PRF_IDX waddr[WRITE_PORTS-1:0],
    input word_t wdata[WRITE_PORTS-1:0]

);
  word_t regs[`PRF_SIZE-1:0];
  word_t read_regs[(2**`PRF_AW)-1:0] /*verilator public_flat*/;
  genvar gi;
  generate
    for (gi = 0; gi < READ_PORTS; gi = gi + 1) begin : Read
      assign rdata[gi] = read_regs[raddr[gi]];
    end
    assign regs[0] = 'b0;
    assign read_regs[0] = 'b0;

    for (gi = 1; gi < `PRF_SIZE; gi = gi + 1) begin : REGS
      assign read_regs[gi] = regs[gi];
      logic [WRITE_PORTS-1:0] w_sel;
      word_t w_data;
      always_comb begin
        integer i;
        for (i = 0; i < WRITE_PORTS; i = i + 1) begin
          w_sel[i] = wvalid[i] & (waddr[i] == gi);
        end
      end
      MUX_OH #(WRITE_PORTS, `WORD_BITS) mux_w_data (
          .sel (w_sel),
          .din (wdata),
          .dout(w_data)
      );
      reg_l #(`WORD_BITS) phy_data_reg (
          .load(|w_sel),
          .din (w_data),
          .dout(regs[gi]),
          .*
      );
    end

    for (gi = `PRF_SIZE; gi < 2 ** `PRF_AW; gi = gi + 1) begin : UnImplementRegs
      assign read_regs[gi] = 'b0;
    end
  endgenerate
`ifdef REGFILE_DIFFTEST

  word_t ref_regs[`PRF_SIZE-1:0];

  always_ff @(posedge clk) begin
    integer i;
    word_t  ref_rdata;
    for (i = 0; i < READ_PORTS; i = i + 1) begin
      ref_rdata = 'b0;
      if (raddr[i] < `PRF_SIZE) ref_rdata = ref_regs[raddr[i]];
      if (rdata[i] != ref_rdata) begin
        $display(" Regfile error ");
        $finish;
      end
    end
    for (i = 0; i < WRITE_PORTS; i = i + 1) begin
      if (wvalid[i]) ref_regs[waddr[i]] <= wdata[i];
    end
  end
`endif
endmodule
